package com.demon.androidbasic.widget

import android.animation.ValueAnimator
import android.annotation.SuppressLint
import android.app.Activity
import android.content.Context
import android.graphics.Color
import android.graphics.drawable.ColorDrawable
import android.graphics.drawable.Drawable
import android.os.Build
import android.util.SparseArray
import android.view.*
import android.widget.*
import androidx.annotation.*
import androidx.core.content.ContextCompat
import androidx.core.widget.PopupWindowCompat
import com.demon.androidbasic.action.AnimAction
import com.demon.androidbasic.action.ClickAction
import com.demon.androidbasic.action.HandlerAction
import com.demon.androidbasic.action.ResourcesAction
import java.lang.ref.SoftReference
import java.util.*

/**
 * author : Android 轮子哥
 * github : https://github.com/getActivity/AndroidProject
 * time   : 2019/09/16
 * desc   : PopupWindow 基类
 */
class BasePopupWindow constructor(private val myContext: Context) : PopupWindow(myContext),
    HandlerAction, ClickAction, AnimAction, PopupWindow.OnDismissListener {
    private var mPopupBackground: PopupBackground? = null
    private var mShowListeners: MutableList<OnShowListener?>? = null
    private var mDismissListeners: MutableList<OnDismissListener?>? = null

    /**
     * 设置一个销毁监听器
     *
     * @param listener       销毁监听器对象
     */
    @Deprecated("请使用 {@link #addOnDismissListener(BasePopupWindow.OnDismissListener)}")
    override fun setOnDismissListener(listener: PopupWindow.OnDismissListener?) {
        if (listener == null) {
            return
        }
        addOnDismissListener(DismissListenerWrapper(listener))
    }

    /**
     * 添加一个显示监听器
     *
     * @param listener      监听器对象
     */
    fun addOnShowListener(listener: OnShowListener?) {
        if (mShowListeners == null) {
            mShowListeners = ArrayList()
        }
        mShowListeners!!.add(listener)
    }

    /**
     * 添加一个销毁监听器
     *
     * @param listener      监听器对象
     */
    fun addOnDismissListener(listener: OnDismissListener?) {
        if (mDismissListeners == null) {
            mDismissListeners = ArrayList()
            super.setOnDismissListener(this)
        }
        mDismissListeners!!.add(listener)
    }

    /**
     * 移除一个显示监听器
     *
     * @param listener      监听器对象
     */
    fun removeOnShowListener(listener: OnShowListener?) {
        if (mShowListeners != null) {
            mShowListeners!!.remove(listener)
        }
    }

    /**
     * 移除一个销毁监听器
     *
     * @param listener      监听器对象
     */
    fun removeOnDismissListener(listener: OnDismissListener?) {
        if (mDismissListeners != null) {
            mDismissListeners!!.remove(listener)
        }
    }

    /**
     * 设置显示监听器集合
     */
    private fun setOnShowListeners(listeners: MutableList<OnShowListener?>?) {
        mShowListeners = listeners
    }

    /**
     * 设置销毁监听器集合
     */
    private fun setOnDismissListeners(listeners: MutableList<OnDismissListener?>?) {
        super.setOnDismissListener(this)
        mDismissListeners = listeners
    }

    /**
     * [PopupWindow.OnDismissListener]
     */
    override fun onDismiss() {
        if (mDismissListeners != null) {
            for (listener: OnDismissListener? in mDismissListeners!!) {
                listener!!.onDismiss(this)
            }
        }
    }

    override fun showAsDropDown(anchor: View, xOff: Int, yOff: Int, gravity: Int) {
        if (isShowing || contentView == null) {
            return
        }
        if (mShowListeners != null) {
            for (listener: OnShowListener? in mShowListeners!!) {
                listener!!.onShow(this)
            }
        }
        super.showAsDropDown(anchor, xOff, yOff, gravity)
    }

    override fun showAtLocation(parent: View, gravity: Int, x: Int, y: Int) {
        if (isShowing || contentView == null) {
            return
        }
        if (mShowListeners != null) {
            for (listener: OnShowListener? in mShowListeners!!) {
                listener!!.onShow(this)
            }
        }
        super.showAtLocation(parent, gravity, x, y)
    }

    override fun dismiss() {
        removeCallbacks()
        super.dismiss()
    }

    override fun <V : View> findViewById(@IdRes id: Int): V {
        return contentView.findViewById(id)
    }

    override fun setWindowLayoutType(type: Int) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            super.setWindowLayoutType(type)
        } else {
            PopupWindowCompat.setWindowLayoutType(this, type)
        }
    }

    override fun getWindowLayoutType(): Int {
        return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            super.getWindowLayoutType()
        } else {
            PopupWindowCompat.getWindowLayoutType(this)
        }
    }

    override fun setOverlapAnchor(overlapAnchor: Boolean) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            super.setOverlapAnchor(overlapAnchor)
        } else {
            PopupWindowCompat.setOverlapAnchor(this, overlapAnchor)
        }
    }

    /**
     * 设置背景遮盖层的透明度
     */
    fun setBackgroundDimAmount(@FloatRange(from = 0.0, to = 1.0) dimAmount: Float) {
        val alpha: Float = 1 - dimAmount
        if (isShowing) {
            setActivityAlpha(alpha)
        }
        if (mPopupBackground == null && alpha != 1f) {
            mPopupBackground = PopupBackground()
            addOnShowListener(mPopupBackground)
            addOnDismissListener(mPopupBackground)
        }
        if (mPopupBackground != null) {
            mPopupBackground!!.setAlpha(alpha)
        }
    }

    /**
     * 设置 Activity 窗口透明度
     */
    private fun setActivityAlpha(alpha: Float) {
        if (myContext is Activity) {
            val activity = myContext
            val params: WindowManager.LayoutParams = activity.window.attributes
            val animator: ValueAnimator = ValueAnimator.ofFloat(params.alpha, alpha)
            animator.duration = 300
            animator.addUpdateListener { animation: ValueAnimator ->
                val value: Float = animation.animatedValue as Float
                if (value != params.alpha) {
                    params.alpha = value
                    activity.window.attributes = params
                }
            }
            animator.start()
        }
    }

    open class Builder<B : Builder<B>>(private val mContext: Context) : ResourcesAction, ClickAction {

        override val myContext: Context
            get() = mContext
        /**
         * 获取 PopupWindow 的根布局
         */
        /** PopupWindow 布局  */
        var contentView: View? = null
            private set
        /**
         * 获取当前 PopupWindow 对象
         */
        /** PopupWindow 对象  */
        var popupWindow: BasePopupWindow? = null
            private set

        /** PopupWindow Show 监听  */
        private var mOnShowListeners: MutableList<OnShowListener?>? = null

        /** PopupWindow Dismiss 监听  */
        private var mOnDismissListeners: MutableList<OnDismissListener?>? = null

        /** 动画  */
        private var mAnimations: Int = AnimAction.ANIM_DEFAULT

        /** 位置  */
        private var mGravity: Int = DEFAULT_ANCHORED_GRAVITY

        /** 宽度和高度  */
        private var mWidth: Int = ViewGroup.LayoutParams.WRAP_CONTENT
        private var mHeight: Int = ViewGroup.LayoutParams.WRAP_CONTENT

        /** 是否可触摸  */
        private var mTouchable: Boolean = true

        /** 是否有焦点  */
        private var mFocusable: Boolean = true

        /** 是否外层可触摸  */
        private var mOutsideTouchable: Boolean = false

        /** 背景遮盖层透明度  */
        private var mBackgroundDimAmount: Float = 0f

        /** X 轴偏移  */
        private var mXOffset: Int = 0

        /** Y 轴偏移  */
        private var mYOffset: Int = 0

        /** 点击事件集合  */
        private var mClickArray: SparseArray<OnClickListener<View>>? = null

        /**
         * 设置布局
         */
        fun setContentView(@LayoutRes id: Int): B {
            // 这里解释一下，为什么要传 new FrameLayout，因为如果不传的话，XML 的根布局获取到的 LayoutParams 对象会为空，也就会导致宽高解析不出来
            return setContentView(
                LayoutInflater.from(mContext).inflate(id, FrameLayout(mContext), false)
            )
        }

        fun setContentView(view: View?): B {
            contentView = view
            if (isCreated) {
                popupWindow!!.contentView = view
            } else {
                if (contentView != null) {
                    val params: ViewGroup.LayoutParams? = contentView!!.layoutParams
                    if ((params != null) && (mWidth == ViewGroup.LayoutParams.WRAP_CONTENT) && (mHeight == ViewGroup.LayoutParams.WRAP_CONTENT)) {
                        // 如果当前 PopupWindow 的宽高设置了自适应，就以布局中设置的宽高为主
                        setWidth(params.width)
                        setHeight(params.height)
                    }

                    // 如果当前没有设置重心，就自动获取布局重心
                    if (mGravity == DEFAULT_ANCHORED_GRAVITY) {
                        when (params) {
                            is FrameLayout.LayoutParams -> {
                                setGravity(params.gravity)
                            }
                            is LinearLayout.LayoutParams -> {
                                setGravity(params.gravity)
                            }
                            else -> {
                                // 默认重心是居中
                                setGravity(Gravity.CENTER)
                            }
                        }
                    }
                }
            }
            return this as B
        }

        /**
         * 设置重心位置
         */
        fun setGravity(gravity: Int): B {
            // 适配 Android 4.2 新特性，布局反方向（开发者选项 - 强制使用从右到左的布局方向）
            mGravity = Gravity.getAbsoluteGravity(
                gravity,
                resources.configuration.layoutDirection
            )
            return this as B
        }

        /**
         * 设置宽度
         */
        fun setWidth(width: Int): B {
            mWidth = width
            if (isCreated) {
                popupWindow!!.width = width
            } else {
                val params: ViewGroup.LayoutParams? =
                    if (contentView != null) contentView!!.getLayoutParams() else null
                if (params != null) {
                    params.width = width
                    contentView!!.setLayoutParams(params)
                }
            }
            return this as B
        }

        /**
         * 设置高度
         */
        fun setHeight(height: Int): B {
            mHeight = height
            if (isCreated) {
                popupWindow!!.height = height
            } else {
                // 这里解释一下为什么要重新设置 LayoutParams
                // 因为如果不这样设置的话，第一次显示的时候会按照 PopupWindow 宽高显示
                // 但是 Layout 内容变更之后就不会按照之前的设置宽高来显示
                // 所以这里我们需要对 View 的 LayoutParams 也进行设置
                val params: ViewGroup.LayoutParams? =
                    if (contentView != null) contentView!!.getLayoutParams() else null
                if (params != null) {
                    params.height = height
                    contentView!!.setLayoutParams(params)
                }
            }
            return this as B
        }

        /**
         * 是否可触摸
         */
        fun setTouchable(touchable: Boolean): B {
            mTouchable = touchable
            return this as B
        }

        /**
         * 是否有焦点
         */
        fun setFocusable(focusable: Boolean): B {
            mFocusable = focusable
            return this as B
        }

        /**
         * 是否外层可触摸
         */
        fun setOutsideTouchable(touchable: Boolean): B {
            mOutsideTouchable = touchable
            return this as B
        }

        /**
         * 设置水平偏移量
         */
        fun setXOffset(offset: Int): B {
            mXOffset = offset
            return this as B
        }

        /**
         * 设置垂直偏移量
         */
        fun setYOffset(offset: Int): B {
            mYOffset = offset
            return this as B
        }

        /**
         * 设置动画，已经封装好几种样式，具体可见[AnimAction]类
         */
        fun setAnimStyle(@StyleRes id: Int): B {
            mAnimations = id
            if (isCreated) {
                popupWindow!!.animationStyle = id
            }
            return this as B
        }

        /**
         * 设置背景遮盖层的透明度
         */
        fun setBackgroundDimAmount(@FloatRange(from = 0.0, to = 1.0) dimAmount: Float): B {
            mBackgroundDimAmount = dimAmount
            if (isShowing) {
                popupWindow!!.setBackgroundDimAmount(dimAmount)
            }
            return this as B
        }

        /**
         * 添加显示监听
         */
        fun addOnShowListener(listener: OnShowListener): B {
            if (isCreated) {
                popupWindow!!.addOnShowListener(listener)
            } else {
                if (mOnShowListeners == null) {
                    mOnShowListeners = ArrayList()
                }
                mOnShowListeners!!.add(listener)
            }
            return this as B
        }

        /**
         * 添加销毁监听
         */
        fun addOnDismissListener(listener: OnDismissListener): B {
            if (isCreated) {
                popupWindow!!.addOnDismissListener(listener)
            } else {
                if (mOnDismissListeners == null) {
                    mOnDismissListeners = ArrayList()
                }
                mOnDismissListeners!!.add(listener)
            }
            return this as B
        }

        /**
         * 设置文本
         */
        fun setText(@IdRes viewId: Int, @StringRes stringId: Int): B {
            return setText(viewId, getString(stringId))
        }

        fun setText(@IdRes id: Int, text: CharSequence?): B {
            (findViewById<View>(id) as TextView).text = text
            return this as B
        }

        /**
         * 设置文本颜色
         */
        fun setTextColor(@IdRes id: Int, @ColorInt color: Int): B {
            (findViewById<View>(id) as TextView).setTextColor(color)
            return this as B
        }

        /**
         * 设置提示
         */
        fun setHint(@IdRes viewId: Int, @StringRes stringId: Int): B {
            return setHint(viewId, getString(stringId))
        }

        fun setHint(@IdRes id: Int, text: CharSequence?): B {
            (findViewById<View>(id) as TextView).setHint(text)
            return this as B
        }

        /**
         * 设置可见状态
         */
        fun setVisibility(@IdRes id: Int, visibility: Int): B {
            findViewById<View>(id).visibility = visibility
            return this as B
        }

        /**
         * 设置背景
         */
        fun setBackground(@IdRes viewId: Int, @DrawableRes drawableId: Int): B {
            return setBackground(viewId, ContextCompat.getDrawable(mContext, drawableId))
        }

        fun setBackground(@IdRes id: Int, drawable: Drawable?): B {
            findViewById<View>(id).background = drawable
            return this as B
        }

        /**
         * 设置图片
         */
        fun setImageDrawable(@IdRes viewId: Int, @DrawableRes drawableId: Int): B {
            return setBackground(viewId, ContextCompat.getDrawable(mContext, drawableId))
        }

        fun setImageDrawable(@IdRes id: Int, drawable: Drawable?): B {
            (findViewById<View>(id) as ImageView).setImageDrawable(drawable)
            return this as B
        }

        /**
         * 设置点击事件
         */
        fun setOnClickListener(@IdRes id: Int, listener: OnClickListener<View>): B {
            if (isCreated) {
                popupWindow!!.findViewById<View>(id).setOnClickListener(
                    ViewClickWrapper(
                        popupWindow, listener
                    )
                )
            } else {
                if (mClickArray == null) {
                    mClickArray = SparseArray()
                }
                mClickArray!!.put(id, listener)
            }
            return this as B
        }

        /**
         * 创建
         */
        @SuppressLint("RtlHardcoded")
        fun create(): BasePopupWindow {

            // 判断布局是否为空
            if (contentView == null) {
                throw IllegalArgumentException("are you ok?")
            }

            // 如果当前没有设置重心，就设置一个默认的重心
            if (mGravity == DEFAULT_ANCHORED_GRAVITY) {
                mGravity = Gravity.CENTER
            }

            // 如果当前没有设置动画效果，就设置一个默认的动画效果
            if (mAnimations == AnimAction.ANIM_DEFAULT) {
                mAnimations = when (mGravity) {
                    Gravity.TOP -> AnimAction.ANIM_TOP
                    Gravity.BOTTOM -> AnimAction.ANIM_BOTTOM
                    Gravity.LEFT -> AnimAction.ANIM_LEFT
                    Gravity.RIGHT -> AnimAction.ANIM_RIGHT
                    else -> AnimAction.ANIM_DEFAULT
                }
            }
            popupWindow = createPopupWindow(mContext)
            popupWindow!!.contentView = contentView
            popupWindow!!.width = mWidth
            popupWindow!!.height = mHeight
            popupWindow!!.animationStyle = mAnimations
            popupWindow!!.isTouchable = mTouchable
            popupWindow!!.isFocusable = mFocusable
            popupWindow!!.isOutsideTouchable = mOutsideTouchable
            popupWindow!!.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
            if (mOnShowListeners != null) {
                popupWindow!!.setOnShowListeners(mOnShowListeners)
            }
            if (mOnDismissListeners != null) {
                popupWindow!!.setOnDismissListeners(mOnDismissListeners)
            }
            popupWindow!!.setBackgroundDimAmount(mBackgroundDimAmount)
            var i = 0
            while (mClickArray != null && i < mClickArray!!.size()) {
                contentView!!.findViewById<View>(mClickArray!!.keyAt(i)).setOnClickListener(
                    ViewClickWrapper(
                        popupWindow, mClickArray!!.valueAt(i)
                    )
                )
                i++
            }
            return popupWindow!!
        }

        /**
         * 显示为下拉
         */
        fun showAsDropDown(anchor: View): BasePopupWindow {
            if (!isCreated) {
                create()
            }
            popupWindow!!.showAsDropDown(anchor, mXOffset, mYOffset, mGravity)
            return popupWindow!!
        }

        /**
         * 显示在指定位置
         */
        fun showAtLocation(parent: View): BasePopupWindow {
            if (!isCreated) {
                create()
            }
            popupWindow!!.showAtLocation(parent, mGravity, mXOffset, mYOffset)
            return popupWindow!!
        }

        /**
         * 当前 PopupWindow 是否创建了
         */
        val isCreated: Boolean
            get() {
                return popupWindow != null
            }

        /**
         * 当前 PopupWindow 是否显示了
         */
        val isShowing: Boolean
            get() {
                return popupWindow != null && popupWindow!!.isShowing
            }

        /**
         * 销毁当前 PopupWindow
         */
        fun dismiss() {
            if (popupWindow != null) {
                popupWindow!!.dismiss()
            }
        }

        /**
         * 创建 PopupWindow 对象（子类可以重写此方法来改变 PopupWindow 类型）
         */
        protected fun createPopupWindow(context: Context): BasePopupWindow {
            return BasePopupWindow(context)
        }

        /**
         * 根据 id 查找 View
         */
        override fun <V : View> findViewById(@IdRes id: Int): V {
            if (contentView == null) {
                // 没有 setContentView 就想 findViewById ?
                throw IllegalStateException("are you ok?")
            }
            return contentView!!.findViewById(id)
        }

        /**
         * 延迟执行
         */
        fun post(r: Runnable?) {
            if (isShowing) {
                popupWindow!!.post(r)
            } else {
                addOnShowListener(ShowPostWrapper(r))
            }
        }

        /**
         * 延迟一段时间执行
         */
        fun postDelayed(r: Runnable?, delayMillis: Long) {
            if (isShowing) {
                popupWindow!!.postDelayed(r, delayMillis)
            } else {
                addOnShowListener(ShowPostDelayedWrapper(r, delayMillis))
            }
        }

        /**
         * 在指定的时间执行
         */
        fun postAtTime(r: Runnable?, uptimeMillis: Long) {
            if (isShowing) {
                popupWindow!!.postAtTime(r, uptimeMillis)
            } else {
                addOnShowListener(ShowPostAtTimeWrapper(r, uptimeMillis))
            }
        }

        companion object {
            private const val DEFAULT_ANCHORED_GRAVITY: Int = Gravity.TOP or Gravity.START
        }
    }

    /**
     * PopupWindow 背景遮盖层实现类
     */
    private class PopupBackground : OnShowListener, OnDismissListener {
        private var mAlpha: Float = 0f
        fun setAlpha(alpha: Float) {
            mAlpha = alpha
        }

        override fun onShow(popupWindow: BasePopupWindow) {
            popupWindow.setActivityAlpha(mAlpha)
        }

        override fun onDismiss(popupWindow: BasePopupWindow) {
            popupWindow.setActivityAlpha(1f)
        }
    }

    /**
     * 销毁监听包装类
     */
    private class DismissListenerWrapper(referent: PopupWindow.OnDismissListener) :
        SoftReference<PopupWindow.OnDismissListener?>(referent), OnDismissListener {
        override fun onDismiss(popupWindow: BasePopupWindow) {
            // 在横竖屏切换后监听对象会为空
            if (get() != null) {
                get()!!.onDismiss()
            }
        }
    }

    /**
     * 点击事件包装类
     */
    private class ViewClickWrapper(
        private val mBasePopupWindow: BasePopupWindow?,
        private val mListener: OnClickListener<View>
    ) : View.OnClickListener {
        override fun onClick(v: View) {
            mListener.onClick(mBasePopupWindow, v)
        }
    }

    /**
     * post 任务包装类
     */
    private class ShowPostWrapper(private val mRunnable: Runnable?) : OnShowListener {
        override fun onShow(popupWindow: BasePopupWindow) {
            if (mRunnable != null) {
                popupWindow.removeOnShowListener(this)
                popupWindow.post(mRunnable)
            }
        }
    }

    /**
     * postDelayed 任务包装类
     */
    private class ShowPostDelayedWrapper(
        private val mRunnable: Runnable?,
        private val mDelayMillis: Long
    ) : OnShowListener {
        override fun onShow(popupWindow: BasePopupWindow) {
            if (mRunnable != null) {
                popupWindow.removeOnShowListener(this)
                popupWindow.postDelayed(mRunnable, mDelayMillis)
            }
        }
    }

    /**
     * postAtTime 任务包装类
     */
    private class ShowPostAtTimeWrapper(
        private val mRunnable: Runnable?,
        private val mUptimeMillis: Long
    ) : OnShowListener {
        override fun onShow(popupWindow: BasePopupWindow) {
            if (mRunnable != null) {
                popupWindow.removeOnShowListener(this)
                popupWindow.postAtTime(mRunnable, mUptimeMillis)
            }
        }
    }

    /**
     * 点击监听器
     */
    interface OnClickListener<V : View?> {
        fun onClick(popupWindow: BasePopupWindow?, view: V)
    }

    /**
     * 显示监听器
     */
    interface OnShowListener {
        /**
         * PopupWindow 显示了
         */
        fun onShow(popupWindow: BasePopupWindow)
    }

    /**
     * 销毁监听器
     */
    interface OnDismissListener {
        /**
         * PopupWindow 销毁了
         */
        fun onDismiss(popupWindow: BasePopupWindow)
    }
}